/********************************************************************
        File :			Listener.cpp
        Creation date :	2010/6/27
		
        License :			Copyright 2010 Ahmed Charfeddine, http://www.pushframework.com

                                   Licensed under the Apache License, Version 2.0 (the "License");
                                   you may not use this file except in compliance with the License.
                                   You may obtain a copy of the License at
				
                                           http://www.apache.org/licenses/LICENSE-2.0
				
                                   Unless required by applicable law or agreed to in writing, software
                                   distributed under the License is distributed on an "AS IS" BASIS,
                                   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                                   See the License for the specific language governing permissions and
                                   limitations under the License.
	
	
 *********************************************************************/
#include "StdAfx.h"
#include "Listener.h"



namespace PushFramework {

    Listener::Listener() {
    }

    Listener::~Listener() {
    }

    bool Listener::startListening() {
        //Create the socket :
        hSocket = socket(AF_INET, SOCK_STREAM, 0);
        if (hSocket < 0) {
            cout << "unable to create listening socket" << endl;
            return false;
        }

        //Set as non-blocking :
        if (fcntl(hSocket, F_SETFL, O_NONBLOCK)) {
            cout << "Could not make the socket non-blocking" << endl;
            close(hSocket);
            return false;
        }


        int on = 1;
        if (setsockopt(hSocket, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on))) {
            cout << "Could not set socket %d option for reusability" << endl;
            close(hSocket);
            return false;
        }



        //Prepare address to bind to :
        SOCKADDR_IN saServer;
        saServer.sin_port = htons(uPort);
        saServer.sin_family = AF_INET;
        if (options.interfaceAddress != NULL) {
            u_long nInterfaceAddr = inet_addr(options.interfaceAddress);
            if (nInterfaceAddr != INADDR_NONE) {
                close(hSocket);
                return false;
            }
            saServer.sin_addr.s_addr = nInterfaceAddr;
        } else
            saServer.sin_addr.s_addr = INADDR_ANY;


        //Bind the socket :
        int nRet = bind(hSocket, (struct sockaddr *) &saServer, sizeof (struct sockaddr));
        if (nRet < 0) {
            cout << "Could not bind socket to address" << endl;
            close(hSocket);
            return false;
        }






        //Set socket to listening mode :
        if (listen(hSocket, options.listeningBackLog)) {
            cout << "unable to listen" << endl;
            close(hSocket);
            return false;
        }





        //Create kill event used by ::stop to terminate the listening thread
        stopListen = false;



        int iret1 = pthread_create(&hThread, NULL, threadProc, (void*) this);


        return true;
    }

    void Listener::stopListening() {
        stopListen = true;
        close(hSocket);
        cout << "waiting for listening thread to stop" << endl;
        pthread_join(hThread, NULL);
        cout << "stopListener ok" << endl;
    }

    void* Listener::threadProc(void* lpVoid)
    {
        Listener* pThis = reinterpret_cast<Listener*> (lpVoid);
        pThis->doListen();
        return 0;
    }

    bool Listener::acceptConnectionRequest(SOCKET clientSocket, SOCKADDR_IN SockAddr) {

        //Set as non-blocking :
        if (fcntl(clientSocket, F_SETFL, O_NONBLOCK)) {
            cout << "Could not make the socket non-blocking" << endl;
            shutdown(clientSocket, 2);
            close(clientSocket);
            return true;
        }


        //Start servicing new connection
        if (!handleAcceptedSocket(clientSocket, SockAddr)) {
            shutdown(clientSocket, 2);
            close(clientSocket);
            return true;
        }
        return true;
    }

    void Listener::setListeningPort(unsigned int uPort) {
        this->uPort = uPort;
    }

    void Listener::doListen() {

        struct sockaddr_in cli_addr;
        socklen_t clilen = sizeof (cli_addr);

        while (!stopListen) {
            int clientSocket = accept(hSocket,
                    (struct sockaddr *) &cli_addr, &clilen);

            if (clientSocket < 0) {
                if (errno == EAGAIN) {
                    continue;
                }
                perror("Error on accept : ");
                //cout << "Error on accept : " << errno << endl;
                break;
                //if(errno != EINTR && errno != EAGAIN)
            }
            acceptConnectionRequest(clientSocket, cli_addr);
        }
        close(hSocket);

    }

    ListenerOptions& Listener::getOptions() {
        return options;
    }

}
